#ifndef _cesium_buffer_hpp_
#define _cesium_buffer_hpp_

#include "cesium/platform.hpp"
#include "cesium/refcounted.hpp"

namespace Cesium {

struct BufferRange {
    uint32 begin;
    uint32 length;

    BufferRange(uint32 b, uint32 len)
     : begin(b), length(len) {}

}; // struct BufferRange

/** Base class for buffers used for exchanging data between sources and sinks.
 *  The interface provides guaranteed contiguous buffers so they can safely
 *  be accessed directly based on a buffer provided base pointer.
 */
class Buffer : public RefCounted<Buffer> {
public:
    Buffer();
    Buffer(uint32 cnt);
    Buffer(const Buffer& cpy);

    virtual ~Buffer();

    /** Returns the raw buffer pointer. */
    uint8* raw() const;

    /** Returns the size of the buffer. */
    uint32 size() const;

    /** Request to resize the buffer.  Returns the new size of the buffer, which
     *  is not guaranteed to be as large as requested.
     */
    uint32 resize(uint32 sz);

    /** Returns the specified element of the buffer. */
    uint8& operator[](uint32 idx);
    /** Returns the specified element of the buffer. */
    const uint8& operator[](uint32 idx) const;

    /** Sets a subset of the buffer from the source. */
    void set(uint8* src, uint32 base, uint32 len);
    /** Sets the contents of the buffer from the source. */
    void set(uint8* src);

    Buffer& operator=(const Buffer& cpy);
protected:
    /** Allocate memory for this buffer.
     *  \param bytes the number of bytes requested to be allocated
     *  \param allocated_bytes
     *  \param allocated_mem returns the pointer to the new buffer, if successful
     */
    typedef void (*AllocateFuncPtr)(uint32 bytes, uint32* allocated_bytes, uint8** allocated_mem);

    /** Deallocate memory previously allocated for this buffer.
     *  \param buf pointer to the buffer to deallocate
     */
    typedef void (*DeallocateFuncPtr)(uint8* buf);

    struct VTable {
        VTable(AllocateFuncPtr alloc_func, DeallocateFuncPtr dealloc_func) {
            alloc = alloc_func;
            dealloc = dealloc_func;
        }

        AllocateFuncPtr alloc;
        DeallocateFuncPtr dealloc;
    };

    // Constructor used to provide alternative allocator.
    Buffer(VTable* vt);

    void allocate(uint32 bytes, uint32* allocated_bytes, uint8** allocated_mem);
    void deallocate(uint8* buf);

    uint8* buffer;
    uint32 count;
    VTable* vtptr;

private:
    static VTable defaultVTable;
}; // class Buffer



/** PinnedBuffer uses an externally provided buffer as storage.  It is fixed
 *  size - resize() always fails.  It doesn't perform any of its own
 *  allocation or deallocation.
 */
class PinnedBuffer : public Buffer {
public:
    template<typename T>
    PinnedBuffer(T* bufobject)
     : Buffer(&PinnedBuffer::defaultVTable)
    {
        count = sizeof(T);
        buffer = (uint8*)bufobject;
    }

    PinnedBuffer(uint8* buf, uint32 len);
    virtual ~PinnedBuffer();
private:
    static VTable defaultVTable;
}; // class PinnedBuffer

} // namespace Cesium

#endif //_cesium_buffer_hpp_
